/*******************************************************************************
 * FILE			FreePhotoBooth.cpp
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This C++ file contains most of the code which controls the behavior of the 
 * application. The naming-scheme is as follows:
 * FunctionsAreNamedLikeThis()
 * variablesAreNamedLikeThis
 *			- Excluding variables of Mode *, which are LikeThis
******************************************************************************/
#include "FreePhotoBooth.h"
#include "Mode.h"

/* GLOBALS */

// Pointers for each of the three modes
Mode * EasyMode;
Mode * PhotoMode;
Mode * PrinterMode;

// This array will store all of the Images needed for the countdown timer
Image * images[14];

// This is the number of seconds to delay between each shot
int delayTime;

/* This is a pointer to the first link in the L.L that is used in 
 * Photo and Printer mode */
PBDataStruct * head;

/* The next five variables are used by the Image Processing Thread */
// This is a handle to the Image Processing Thread
HANDLE hImageProcThread;
/* This is a handle to a Mutex, which is used to ensure that no 
 * two threads are reading and writing to the boolean at the same 
 * time. */
HANDLE hImageProcMutex;
/* This boolean is to ensure that two(or more) image proc threads are 
 * not accidentally created. */
boolean imageProcIsRunning;

/* This boolean tells the application whether or not to save images
 * to the users "my pictures" directory */
boolean saving;

/* This string stores the IP Address (v4) of the Photo Mode client.
 * Used when the application is running in Printer Mode. */
char ipAddr[16];

/* The next three variables are used exclusively by Printer Mode */
/* This array of Bitmap pointers keeps track of Bitmaps available to
 * Printer Mode */
Bitmap **availableShots;
/* This array of booleans keeps track of which shots are being printed */
boolean *shotSelected;
/* This integer keeps track of which image is being displayed */
int printModeShot;
/* This integer keeps track of how many shots are avilable */
int numAvailableShots;

// Globals used in creating the main GUI windows
HDC hdc ;
HDC hdcMem;
PAINTSTRUCT ps;
HBITMAP hbm;
RECT rc;

/*******************************************************************************
 * FUNCTION		WinMain
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This is the entry point for a Win32 GUI based application. This is where 
 * the windows are created and messages are fed through. Relevant messages
 * are passed to their appropriate WindowProc, other messages are fed through
 * the message pump.
******************************************************************************/
int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine, int nCmdShow )
{
	
	/* used for testing
	PBDataStruct node1("John1", "S", "filepath");
	PBDataStruct node2("John2", "S", "filepath");
	PBDataStruct node3("John3", "S", "filepath");
	OutputDebugString("Inserting John2 \n");
	node1.insert(&node2);
	OutputDebugString("Inserting John3 \n");
	node1.insert(&node3);
	PBDataStruct * temp;
	temp = node1.find("John2", "S");
	if (temp != nullptr) {
		OutputDebugString(temp->getFirstName());
		OutputDebugString(temp->getLastName());
		OutputDebugString("\n");
	}
	return 0; // end testing */
	HANDLE hServerThread;
	hServerThread= StartServer();


    MSG msg;
	// This is used to startup and shutdown GDI
	ULONG_PTR	gdiplusToken;

	// Create a Mode class for each Window
	EasyMode = new Mode(EASYMODE);
	PhotoMode = new Mode(PHOTOMODE);
	PrinterMode = new Mode(PRINTERMODE);

	// Initialize the delayTime to 5s
	delayTime = 5;

	// Intialize the mutex with no initial owner
    hImageProcMutex = CreateMutex( 
        NULL,              // default security attributes
        FALSE,             // initially not owned
        NULL);             // unnamed mutex
	// check to make sure it was properly created, if it isn't exit
	if (hImageProcMutex == NULL) 
		return 1;
	// acquire mutex
	WaitForSingleObject( 
		hImageProcMutex,    // handle to mutex
        INFINITE);  // no time-out interval
	imageProcIsRunning = false;
	// release mutex
	ReleaseMutex(hImageProcMutex);

	// Initialize GDI+.
	GdiplusStartupInput gdiplusStartupInput;
	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	// Load all of the images into the images[]
	LoadImages();

	// Create the Local Directory which will be used to store pictures
	CreateDirectory(GetLocalDirectory(), NULL);

	// initialize saving to true
	saving = true;

	// initialize the PBDataStruct head to point to nothing
	head = nullptr;

	// initialize the Printer Mode variable pointers to point to nothing
	shotSelected = nullptr;
	availableShots = nullptr;
	numAvailableShots = 0; // initialize to 0
	printModeShot = 0; // initialize this to 0 as well


	// initialize the ip address to home
	strcpy(ipAddr ,"127.0.0.1");

	// Initialize the program in Easy Mode
    ShowWindow ( EasyMode->getWindow(),SW_SHOW);
    UpdateWindow ( EasyMode->getWindow());
	ShowWindow( EasyMode->getCapWindow(),SW_SHOW);
	SendMessage( EasyMode->getCapWindow(),WM_CAP_DRIVER_CONNECT,0,0);
	SendMessage( EasyMode->getCapWindow(), WM_CAP_SET_SCALE, true , 0);
	SendMessage( EasyMode->getCapWindow(), WM_CAP_SET_PREVIEWRATE, 66, 0);
	SendMessage( EasyMode->getCapWindow(), WM_CAP_SET_PREVIEW, true , 0);
	

    while (GetMessage(&msg,0,0,0))
    {
		// see if the Message is for any of my windows
        if (!IsDialogMessage((*EasyMode).getWindow(), &msg) 
			&& !IsDialogMessage((*PhotoMode).getWindow(), &msg) 
			&& !IsDialogMessage((*PrinterMode).getWindow(), &msg))
        {
            TranslateMessage (&msg);
            DispatchMessage (&msg);
        }

    }
	// Wait for the ImageProc thread to terminate, and then close the handle
	WaitForSingleObject(
		hImageProcThread,
		INFINITE );
	CloseHandle(hImageProcThread);
	CloseHandle(hImageProcMutex); // close the mutex

	TerminateThread(hServerThread, 0);
	// DUPLICATE USED FOR TESTINGWait for the ServerProc thread to terminate, and then close the handle
	WaitForSingleObject(
		hServerThread,
		INFINITE );
	CloseHandle(hServerThread);

	// Clean up the files saved to disk, if not wanted
	// Also delete the nodes of the L.L. while traversing
	PBDataStruct * next;
	while (head != nullptr) {
		if(!saving)
			DeleteFile(head->getFilepath());
		next = head->getNextLink();
		delete head;
		head = next;
	}

	GdiplusShutdown(gdiplusToken);
    return msg.wParam;
}
/*******************************************************************************
 * FUNCTION		EasyModeProc
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This is the function which draws and controls UI actions with the GUI for 
 * the "Easy Mode" of the application.
******************************************************************************/
LRESULT CALLBACK EasyModeProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HINSTANCE hInstance = GetModuleHandle(NULL);

	//some buttons
    HWND hButtGo;
    HWND hButtOne;
    HWND hButtTwo;
	HWND hButtThree;

	Graphics * graphics;

	// Thread handler
	DWORD dwThreadId;

    switch (message)                  /* handle the messages */
    {
	// slightly over-ride the Paint message
	case WM_PAINT:
		graphics =  Graphics::FromHWND(hwnd);
		graphics->DrawImage(images[11],360,535);
		return DefWindowProc(hwnd, message, wParam, lParam); // pass the message on to the default proc
	case WM_CTLCOLORSTATIC:
		SetBkMode(hdc,TRANSPARENT);
		return (LRESULT)CreateSolidBrush(0xFFFFFF);
	case WM_CREATE:
	{
		InsertMyMenu(hwnd);
		DrawMenuBar(hwnd);

		hButtGo = CreateWindowEx(0,"BUTTON","GO",WS_CHILD | WS_VISIBLE,
										150 , 535 ,200,100, hwnd,(HMENU)BUTTON_GO,hInstance, 0); // instead of 180 used to be 220
		hButtOne = CreateWindowEx(0,"BUTTON","1",WS_CHILD | WS_VISIBLE,
										150, 20, 50, 50,hwnd,(HMENU)BUTTON_ONE,hInstance, 0);
		hButtTwo = CreateWindowEx(0,"BUTTON","2",WS_CHILD | WS_VISIBLE,
										295, 20, 50, 50,hwnd,(HMENU)BUTTON_TWO,hInstance, 0);
		hButtThree = CreateWindowEx(0,"BUTTON","4",WS_CHILD | WS_VISIBLE,
										440, 20, 50, 50,hwnd,(HMENU)BUTTON_FOUR,hInstance, 0);
		break;
	}

	case WM_COMMAND:
	{
		switch (LOWORD(wParam))
		{
			// The menu functions
			case ID_EASYMODE:
				// They are already in easy mode, this will perform no function
				break;
			case ID_PHOTOMODE:
				// Disconnect the Easy Mode capture window
				ShowWindow(EasyMode->getCapWindow(),SW_HIDE);
				SendMessage(EasyMode->getCapWindow(), WM_CAP_DRIVER_DISCONNECT, 0, 0);
				// Connect the Photo Mode capture window
				ShowWindow(PhotoMode->getCapWindow(), SW_SHOW);
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_DRIVER_CONNECT,0,0);
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_SET_SCALE, true , 0);
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_SET_PREVIEWRATE, 66, 0);
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_SET_PREVIEW, true , 0);
				// Hide the current window, and show a new one);
				ShowWindow(EasyMode->getWindow(), SW_HIDE);
				ShowWindow(PhotoMode->getWindow(), SW_SHOW);
				break;
			case ID_PRINTERMODE:
				// Disconnect the Easy Mode capture window
				ShowWindow(EasyMode->getCapWindow(),SW_HIDE);
				SendMessage(EasyMode->getCapWindow(), WM_CAP_DRIVER_DISCONNECT, 0, 0);
				// Hide the current window, and show a new one);
				ShowWindow(EasyMode->getWindow(), SW_HIDE);
				ShowWindow(PrinterMode->getWindow(), SW_SHOW);
				break;
			case ID_CAMERA:
				capDlgVideoSource(EasyMode->getCapWindow()); 
				break;
			case ID_SAVING:
				int savingReturnValue;
				savingReturnValue = MessageBox(hwnd, "Would you like to save every shot to \"My Documents\\FreePhotoBooth\"?", "Saving", MB_YESNOCANCEL);
				if(savingReturnValue == 6)// yes
					saving = true;
				else if (savingReturnValue == 7)// no
					saving = false;
				// do nothing for cancel(== 2)
				break;
			case ID_IPSETTINGS:
				DialogBox(hInstance, 
                          MAKEINTRESOURCE(IDD_DIALOG1), 
                          hwnd, 
						  (DLGPROC)IPProc);
				break;
			case ID_DELAYTIME:
				DialogBox(hInstance, 
                          MAKEINTRESOURCE(IDD_DIALOG2), 
                          hwnd, 
						  (DLGPROC)DelayTimeProc); 
				break;
			case BUTTON_GO:
			{
				// acquire mutex
				WaitForSingleObject( 
					hImageProcMutex,    // handle to mutex
					INFINITE);  // no time-out interval
				if(!imageProcIsRunning) {
					// release mutex
					ReleaseMutex(hImageProcMutex);

					// Allocate memory for thread data.
					PIMAGEPROCDATA pData = (PIMAGEPROCDATA) LocalAlloc(LPTR,
						sizeof(IMAGEPROCDATA));
					if( pData == NULL ) {
						// If the array allocation fails, the system is out of memory
						// so there is no point in trying to print an error message.
						// Just terminate execution.
						ExitProcess(2);
					}

					pData->mode = EASYMODE;
					pData->numberOfShots = (*EasyMode).getNumOfShots();
					hImageProcThread = CreateThread( 
						NULL,                   // default security attributes
						0,                      // use default stack size  
						ImageProc,				// thread function name
						pData,					// argument to thread function 
						0,						// Use default parameters
						&dwThreadId);			// returns the thread identifier

					// Check the return value for success.
					// If CreateThread fails, terminate execution. 
					// This will automatically clean up threads and memory. 
					if (hImageProcThread == NULL) 
					{
						//ErrorHandler(TEXT("CreateThread"));  This function needs to be created
						ExitProcess(3);
					}
				}
				else
					ReleaseMutex(hImageProcMutex);

				break;
			}

			case BUTTON_ONE:
			{
				//Set the number of shots to 1
				(*EasyMode).setNumOfShots(1);
				break;
			}
			case BUTTON_TWO:
			{
				// Set the number of shots to 2
				(*EasyMode).setNumOfShots(2);	
				break;
			}
			case BUTTON_FOUR:
			{
				// Set the number of shots to 4
				(*EasyMode).setNumOfShots(4);	
				break;
			}
		}
		break;
	}

	case WM_DESTROY:
	{

		SendMessage((*EasyMode).getCapWindow(), WM_CAP_DRIVER_DISCONNECT, 0, 0);
		PostQuitMessage(0);   /* send a WM_QUIT to the message queue */
		break;
	}

	default:              /* for messages that we don't deal with */
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
    return 0;
}
/*******************************************************************************
 * FUNCTION		PhotoModeProc
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This is the function which draws and controls UI actions with the GUI for 
 * the "Photo Mode" of the application.
******************************************************************************/
LRESULT CALLBACK PhotoModeProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HINSTANCE hInstance = GetModuleHandle(NULL);
	
	//some buttons
    HWND hFirstNameLabel;
    HWND hFirstNameText;
    HWND hLastNameLabel;
	HWND hLastNameText;
	HWND hButtGo;

	// Thread handler
	DWORD dwThreadId;

	Graphics * graphics;

    switch (message)                  /* handle the messages */
    {
	// slightly over-ride the Paint message
	case WM_PAINT:
		graphics =  Graphics::FromHWND(hwnd);
		graphics->DrawImage(images[11],360,535);
		return DefWindowProc(hwnd, message, wParam, lParam); // pass the message on to the default proc
    case WM_CTLCOLORSTATIC:
        SetBkMode(hdc,TRANSPARENT);
        return (LRESULT)TRANSPARENT;

    case WM_CREATE:
    {
		InsertMyMenu(hwnd);
		DrawMenuBar(hwnd);

		hFirstNameLabel = CreateWindowEx(0,"STATIC","First Name: ",  WS_CHILD| WS_VISIBLE,
                                      10,10,100,20,hwnd,NULL,hInstance, 0);
        hFirstNameText = CreateWindowEx(0,"EDIT","",WS_CHILD | WS_VISIBLE,
                                       90,10,100,20,hwnd,NULL,hInstance, 0);
		hLastNameLabel = CreateWindowEx(0,"STATIC","Last Name: ",  WS_CHILD| WS_VISIBLE,
                                      10,50,100,20,hwnd,NULL,hInstance, 0);
		hLastNameText = CreateWindowEx(0,"EDIT","",WS_CHILD | WS_VISIBLE,
                                       90,50,100,20,hwnd,NULL,hInstance, 0);
		hButtGo = CreateWindowEx(0,"BUTTON","GO",WS_CHILD | WS_VISIBLE,
										150 , 535 ,200,100, hwnd,(HMENU)BUTTON_GO,hInstance, 0);
        break;
    }

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
			// The menu functions
			case ID_EASYMODE:
				// Disconnect the Photo Mode capture window
				ShowWindow(PhotoMode->getCapWindow(),SW_HIDE);
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_DRIVER_DISCONNECT, 0, 0);
				// Hide the current window, and show a new one);
				ShowWindow(PhotoMode->getWindow(), SW_HIDE);
				ShowWindow(EasyMode->getWindow(), SW_SHOW);
				// Connect the Easy Mode capture window
				ShowWindow(EasyMode->getCapWindow(), SW_SHOW);
				SendMessage(EasyMode->getCapWindow(), WM_CAP_DRIVER_CONNECT,0,0);
				SendMessage(EasyMode->getCapWindow(), WM_CAP_SET_SCALE, true , 0);
				SendMessage(EasyMode->getCapWindow(), WM_CAP_SET_PREVIEWRATE, 66, 0);
				SendMessage(EasyMode->getCapWindow(), WM_CAP_SET_PREVIEW, true , 0);
				break;
			case ID_PHOTOMODE:
				// They are already in photo mode, this will perform no function
				break;
			case ID_PRINTERMODE:
				// Disconnect the Easy Mode capture window
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_DRIVER_DISCONNECT, 0, 0);
				ShowWindow(PhotoMode->getCapWindow(),SW_HIDE);
				// Hide the current window, and show a new one);
				ShowWindow(PhotoMode->getWindow(), SW_HIDE);
				ShowWindow(PrinterMode->getWindow(), SW_SHOW);
				break;
			case ID_CAMERA:
				capDlgVideoSource(PhotoMode->getCapWindow()); 
				break;
			case ID_SAVING:
				int savingReturnValue;
				savingReturnValue = MessageBox(hwnd, "Would you like to save every shot to \"My Documents\\FreePhotoBooth\"?", "Saving", MB_YESNOCANCEL);
				if(savingReturnValue == 6)// yes
					saving = true;
				else if (savingReturnValue == 7)// no
					saving = false;
				// do nothing for cancel(== 2)
				break;
			case ID_IPSETTINGS:
				DialogBox(hInstance, 
                          MAKEINTRESOURCE(IDD_DIALOG1), 
                          hwnd, 
						  (DLGPROC)IPProc);
				break;
			case ID_DELAYTIME:
				DialogBox(hInstance, 
                          MAKEINTRESOURCE(IDD_DIALOG2), 
                          hwnd, 
						  (DLGPROC)DelayTimeProc); 
				break;
			// The buttons displayed on the window
			case BUTTON_GO:
			{
				// The maximum length of firstName or lastName is 100 chars
				char buffer[100];
				// Will store the length that is entered into the fields
				int n;
				HWND hTemp;

				// Get the first name
				hTemp = GetWindow(hwnd, GW_CHILD);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				GetWindowText(hTemp, buffer, 100);
				n = strlen(buffer);
				char * firstName = new char[n+1];
				strcpy(firstName, buffer);

				// Get the last name
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				GetWindowText(hTemp, buffer, 100);
				n = strlen(buffer);
				char * lastName = new char[n+1];
				strcpy(lastName, buffer);
				
				PhotoMode->setShotsTaken(PhotoMode->getShotsTaken() + 1);
				char* filePath = GetFileName(PHOTOMODE);

				PBDataStruct * newLink = new PBDataStruct(firstName, lastName, filePath);

				// if the head hasn't been initialized yet, this PBDataStruct will
				// become the head.
				if (head == nullptr)
					head = newLink;
				// Otherwise insert this PBDataStruct to the L.L.
				else
					head->insert(newLink);

				//delete firstName;
				//delete lastName;
				//delete filePath;

				// acquire mutex
				WaitForSingleObject( 
					hImageProcMutex,    // handle to mutex
					INFINITE);  // no time-out interval
				if(!imageProcIsRunning) {
					// release mutex
					ReleaseMutex(hImageProcMutex);

					// Allocate memory for thread data.
					PIMAGEPROCDATA pData = (PIMAGEPROCDATA) LocalAlloc(LPTR,
						sizeof(IMAGEPROCDATA));
					if( pData == NULL ) {
						// If the array allocation fails, the system is out of memory
						// so there is no point in trying to print an error message.
						// Just terminate execution.
						ExitProcess(2);
					}
					pData->mode = PHOTOMODE;
					pData->numberOfShots = 1;
					hImageProcThread = CreateThread( 
						NULL,                   // default security attributes
						0,                      // use default stack size  
						ImageProc,				// thread function name
						pData,					// argument to thread function 
						0,						// Use default parameters
						&dwThreadId);			// returns the thread identifier

					// Check the return value for success.
					// If CreateThread fails, terminate execution. 
					// This will automatically clean up threads and memory. 
					if (hImageProcThread == NULL) 
					{
						//ErrorHandler(TEXT("CreateThread"));  This function needs to be created
						ExitProcess(3);
					}
				}
				else
					ReleaseMutex(hImageProcMutex);

				break;
			}
        }
        break;
    }

    case WM_DESTROY:
    {
        SendMessage((*PhotoMode).getCapWindow(), WM_CAP_DRIVER_DISCONNECT, 0, 0);
        PostQuitMessage(0);   /* send a WM_QUIT to the message queue */
        break;
    }

    default:              /* for messages that we don't deal with */
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}
/*******************************************************************************
 * FUNCTION		PrinterModeProc
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This is the function which draws and controls UI actions with the GUI for 
 * the "Printer Mode" of the application.
******************************************************************************/
LRESULT CALLBACK PrinterModeProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HINSTANCE hInstance = GetModuleHandle(NULL);

	Graphics * graphics;

	//some buttons
    HWND hFirstNameLabel;
    HWND hFirstNameText;
    HWND hLastNameLabel;
	HWND hLastNameText;
	HWND hButtQuery;
	HWND hButtNext;
	HWND hButtAction;
	HWND hButtPrint;

    switch (message)                  /* handle the messages */
    {

    // slightly over-ride the Paint message
	case WM_PAINT:
		graphics =  Graphics::FromHWND(hwnd);
		graphics->DrawImage(images[13],25, 85, 580, 435);
		return DefWindowProc(hwnd, message, wParam, lParam); // pass the message on to the default proc
	case WM_CTLCOLORSTATIC:
        SetBkMode(hdc,TRANSPARENT);
        return (LRESULT)TRANSPARENT;

    case WM_CREATE:
    {

		InsertMyMenu(hwnd);
		DrawMenuBar(hwnd);

		hFirstNameLabel = CreateWindowEx(0,"STATIC","First Name: ",  WS_CHILD| WS_VISIBLE,
                                      10,10,100,20,hwnd,NULL,hInstance, 0);
        hFirstNameText = CreateWindowEx(0,"EDIT","",WS_CHILD | WS_VISIBLE,
                                       90,10,100,20,hwnd,NULL,hInstance, 0);
		hLastNameLabel = CreateWindowEx(0,"STATIC","Last Name: ",  WS_CHILD| WS_VISIBLE,
                                      10,50,100,20,hwnd,NULL,hInstance, 0);
		hLastNameText = CreateWindowEx(0,"EDIT","",WS_CHILD | WS_VISIBLE,
                                       90,50,100,20,hwnd,NULL,hInstance, 0);
		hButtQuery = CreateWindowEx(0,"BUTTON","Query",WS_CHILD | WS_VISIBLE,
										225 , 15 ,100,50, hwnd,(HMENU)BUTTON_QUERY,hInstance, 0); 
		hButtAction = CreateWindowEx(0,"BUTTON","Select",WS_CHILD | WS_VISIBLE,
										235 , 525 ,65,25, hwnd,(HMENU)BUTTON_ACTION,hInstance, 0);
		hButtNext = CreateWindowEx(0,"BUTTON","Next",WS_CHILD | WS_VISIBLE,
										355 , 525 ,65,25, hwnd,(HMENU)BUTTON_NEXT,hInstance, 0);
		hButtPrint = CreateWindowEx(0,"BUTTON","Print",WS_CHILD | WS_VISIBLE,
										225 , 560 ,200,75, hwnd,(HMENU)BUTTON_PRINT,hInstance, 0);

        break;
    }

    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
			// The menu functions
			case ID_EASYMODE:
				// Connect the Photo Mode capture window
				SendMessage(EasyMode->getCapWindow(), WM_CAP_DRIVER_CONNECT,0,0);
				SendMessage(EasyMode->getCapWindow(), WM_CAP_SET_SCALE, true , 0);
				SendMessage(EasyMode->getCapWindow(), WM_CAP_SET_PREVIEWRATE, 66, 0);
				SendMessage(EasyMode->getCapWindow(), WM_CAP_SET_PREVIEW, true , 0);
				ShowWindow(EasyMode->getCapWindow(), SW_SHOW);
				// Hide the current window, and show a new one);
				ShowWindow(PrinterMode->getWindow(), SW_HIDE);
				ShowWindow ( EasyMode->getWindow(),SW_SHOW);
				UpdateWindow ( EasyMode->getWindow());
				break;
			case ID_PHOTOMODE:
				// Connect the Photo Mode capture window
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_DRIVER_CONNECT,0,0);
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_SET_SCALE, true , 0);
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_SET_PREVIEWRATE, 66, 0);
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_SET_PREVIEW, true , 0);
				ShowWindow(PhotoMode->getCapWindow(), SW_SHOW);
				// Hide the current window, and show a new one);
				ShowWindow(PrinterMode->getWindow(), SW_HIDE);
				ShowWindow(PhotoMode->getWindow(), SW_SHOW);
				break;
			case ID_PRINTERMODE:
				// They are already in Printer mode, this will perform no function
				break;
			case ID_CAMERA:
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_DRIVER_CONNECT,0,0);
				capDlgVideoSource(PhotoMode->getCapWindow()); 
				SendMessage(PhotoMode->getCapWindow(), WM_CAP_DRIVER_DISCONNECT,0,0);
				break;
			case ID_SAVING:
				int savingReturnValue;
				savingReturnValue = MessageBox(hwnd, "Would you like to save every shot to \"My Documents\\FreePhotoBooth\"?", "Saving", MB_YESNOCANCEL);
				if(savingReturnValue == 6)// yes
					saving = true;
				else if (savingReturnValue == 7)// no
					saving = false;
				// do nothing for cancel(== 2)
				break;
			case ID_IPSETTINGS:
				DialogBox(hInstance, 
                          MAKEINTRESOURCE(IDD_DIALOG1), 
                          hwnd, 
						  (DLGPROC)IPProc);
				break;
			case ID_DELAYTIME:
				DialogBox(hInstance, 
                          MAKEINTRESOURCE(IDD_DIALOG2), 
                          hwnd, 
						  (DLGPROC)DelayTimeProc); 
				break;
			case BUTTON_NEXT:
			{
				// first see if availableShots is valid
				if (availableShots == nullptr)
					break;
				printModeShot++;
				// check to see if it is valid
				if (printModeShot >= numAvailableShots)
					printModeShot = 0;

				// display the new Bitmap
				graphics =  Graphics::FromHWND(hwnd);
				graphics->DrawImage(availableShots[printModeShot],25, 85, 580, 435);

				// check to see what text the select button should display
				// either select or deselect

				// first get the appropriate handle for the button
				HWND hTemp;
				hTemp = GetWindow(hwnd, GW_CHILD);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT); 
				// hTemp is now the handle for the select window

				if (shotSelected[printModeShot]) {
					// display deselect
					SetWindowText(hTemp, "Deselect");
				}
				else {
					// display select
					SetWindowText(hTemp, "Select");
				}
				break;
			}
			case BUTTON_ACTION:
			{
				// first see if availableShots is valid
				if (availableShots == nullptr)
					break;
				shotSelected[printModeShot] = !shotSelected[printModeShot];
				// check to see what text the select button should display
				// either select or deselect

				// first get the appropriate handle for the button
				HWND hTemp;
				hTemp = GetWindow(hwnd, GW_CHILD);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT); 
				// hTemp is now the handle for the select window

				if (shotSelected[printModeShot]) {
					// display deselect
					SetWindowText(hTemp, "Deselect");
				}
				else {
					// display select
					SetWindowText(hTemp, "Select");
				}
				break;
			}
			case BUTTON_PRINT:
			{
				// first see if availableShots is valid
				if (availableShots == nullptr)
					break;
				// count how many shots are attempting to be taken
				int count = 0;
				for (int i = 0; i < numAvailableShots; i++) {
					if(shotSelected[i])
						count++;
				}
				if (count == 1 || count == 2 || count == 4) {
					Bitmap* bitmaps[4];
					int index = 0;

					// find out which Bitmaps are being processed
					for (int i = 0; i < numAvailableShots; i++) {
						if(shotSelected[i]) {
							bitmaps[index] = availableShots[i];
							index++;
						}
					}

					Bitmap* finalProduct;
					finalProduct = ProcessBitmaps( bitmaps , count);
					PrintBitmap( finalProduct );

					// free up the memory used, and reinit. everything
					delete availableShots;
					availableShots = nullptr;
					delete shotSelected;
					shotSelected = nullptr;
					printModeShot = 0;
					numAvailableShots = 0;

					// draw the curtains
					graphics =  Graphics::FromHWND(hwnd);
					graphics->DrawImage(images[13],25, 85, 580, 435);
				}
				else // display error to user
					MessageBox(hwnd, "Please select only 1, 2, or 4 shots.\n", 0, 0);

				break;
			}
			case BUTTON_QUERY:
			{
				// The maximum length of firstName or lastName is 100 chars
				char buffer[100];
				// Will store the length that is entered into the fields
				int n;
				HWND hTemp;

				// The WComm class object
				WComm w;

				// Get the first name
				hTemp = GetWindow(hwnd, GW_CHILD);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				GetWindowText(hTemp, buffer, 100);
				n = strlen(buffer);
				char * firstName = new char[n+1];
				strcpy(firstName, buffer);

				// Get the last name
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				hTemp = GetWindow(hTemp, GW_HWNDNEXT);
				GetWindowText(hTemp, buffer, 100);
				n = strlen(buffer);
				char * lastName = new char[n+1];
				strcpy(lastName, buffer);
				
				// Check to see if a local lookup is needed
				if (strcmp(ipAddr, "127.0.0.1") == 0 || strcmp(ipAddr, "0.0.0.0") == 0) {
					PBDataStruct * link = nullptr;
					int count = 0;
					// check the head to make sure that it is valid
					// and count how many shots are available
					if (head != nullptr) {
							link = head;
							link = head->find(firstName, lastName);
							while (link != nullptr) {
									count++;
									if (link->getNextLink() != nullptr)
											link = link->getNextLink();
									else
											break;
									link = link->find(firstName, lastName);
							}
					}

					// Now access the images
					if (count > 0) {
						// check to see if the boolean array is already allocated
						if (shotSelected != nullptr) {
							delete shotSelected;
							shotSelected = nullptr;
						}
						// check to see if the Bitmap pointer array is already allocated
						if (availableShots != nullptr) {
							delete availableShots;
							availableShots = nullptr;
						}
						numAvailableShots = count;
						// create the boolean array
						shotSelected = new boolean[count];

						// create the Bitmap pointer array
						availableShots = new Bitmap*[count];

						/* initialize the boolean array to false
						 * and create an array of Bitmap pointers */
						for (int i = 0; i < count; i++) {
							shotSelected[i] = false;
						}

						link = head;
						link = head->find(firstName, lastName);
						while (link != nullptr) {
								char * fileName;
								fileName = link->getFilepath();
								// Convert to a wchar_t*
								size_t origsize = strlen(fileName) + 1;
								size_t convertedChars = 0;
								wchar_t * wcFileName =  new wchar_t[origsize];
								mbstowcs_s(&convertedChars, wcFileName, origsize, fileName, _TRUNCATE);
								// Bring the Bitmap to memory
								availableShots[count - 1] = Bitmap::FromFile(wcFileName, false);
								delete wcFileName;

								count--;
								if (link->getNextLink() != nullptr)
										link = link->getNextLink();
								else
										break;
								link = link->find(firstName, lastName);
						}
						// Go ahead and display first image
						graphics =  Graphics::FromHWND(hwnd);
						printModeShot = 0;
						graphics->DrawImage(availableShots[0],25, 85, 580, 435);
					}

				}
				// otherwise contact remote server
				else {
					char rec[32] = "";
					char fileName[75] = "";
					char query[207] = ""; // The Max possible query length

					// Connect To Server
					if(w.connectServer(ipAddr,DEFAULT_PORT) != 0)
						break; // end BUTTON_QUERY
				
					// generate query
					strcpy(query, "query ");
					strcat(query, firstName);
					strcat(query, " ");
					strcat(query, lastName);
					// Send Query
					w.sendData(query);	

					// Receive Response
					int count;
					if (w.recvData(rec,32) > 0)
						count = atoi(rec);
					else
						count = 0;


					// if needed allocate some Bitmap pointers
					if(count > 0) {
						// check to see if the boolean array is already allocated
						if (shotSelected != nullptr) {
							delete shotSelected;
							shotSelected = nullptr;
						}
						// check to see if the Bitmap pointer array is already allocated
						if (availableShots != nullptr) {
							for(int i = 0; i < numAvailableShots; i++) {
								delete availableShots[i];
								availableShots[i] = nullptr;
							}
							delete availableShots;
							availableShots = nullptr;
						}

						numAvailableShots = count;

						// create the boolean array
						shotSelected = new boolean[count];
						// initialize the boolean array to false
						for (int i = 0; i < count; i++)
							shotSelected[i] = false;
						// create the Bitmap pointer array
						availableShots = new Bitmap *[count];
					}

					while (count > 0) {
						w.fileReceive(fileName);

						// NOTE: Bitmap::FromFile locks the file...

						// Convert to a wchar_t*
						size_t origsize = strlen(fileName) + 1;
						size_t convertedChars = 0;
						wchar_t * wcFileName =  new wchar_t[origsize];
						mbstowcs_s(&convertedChars, wcFileName, origsize, fileName, _TRUNCATE);

						// Bring the Bitmap to memory
						availableShots[count - 1] = Bitmap::FromFile(wcFileName, false);
						delete wcFileName;

						OutputDebugString(fileName);
						OutputDebugString("\n");

						count--;
						// Go ahead and display first image (if count == 0)
						if (count == 0) {
							graphics =  Graphics::FromHWND(hwnd);
							graphics->DrawImage(availableShots[count],25, 85, 580, 435);
							printModeShot = count;
						}
					}
					w.closeConnection();
				}
				break; // end BUTTON_QUERY
			}
        }
        break;
    }

    case WM_DESTROY:
    {
        PostQuitMessage(0);   /* send a WM_QUIT to the message queue */
        break;
    }

    default:              /* for messages that we don't deal with */
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}
/*******************************************************************************
 * FUNCTION		insertMyMenu
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This is the function which takes in a HWND and appends my menu to it.
 * The menu should look like this in the appliction window:
 *  ______				 __________
 * | Mode |__________	| Settings |__
 * |	Easy Mode	 |	|	Camera	  |
 * |	Photo Mode	 |  |	Printer	  |
 * |	Printer Mode |  ---------------
 * -------------------
******************************************************************************/
void InsertMyMenu(HWND hwnd) {
	HMENU hMenu = CreateMenu();
    SetMenu(hwnd, hMenu);
	HMENU hSubMenu = CreatePopupMenu();
    AppendMenu(hSubMenu, MF_STRING, ID_EASYMODE, "Easy Mode");
    AppendMenu(hSubMenu, MF_STRING, ID_PHOTOMODE, "Photo Mode");
    AppendMenu(hSubMenu, MF_STRING, ID_PRINTERMODE, "Printer Mode");
    InsertMenu(hMenu, 0, MF_POPUP|MF_BYPOSITION, (UINT_PTR)hSubMenu, "Mode");
    DestroyMenu(hSubMenu);

    hSubMenu = CreatePopupMenu();
    AppendMenu(hSubMenu, MF_STRING, ID_CAMERA, "Camera");
   // AppendMenu(hSubMenu, MF_STRING, ID_PRINTER, "Printer");
	AppendMenu(hSubMenu, MF_STRING, ID_SAVING, "Saving");
	AppendMenu(hSubMenu, MF_STRING, ID_IPSETTINGS, "IP Address");
	AppendMenu(hSubMenu, MF_STRING, ID_DELAYTIME, "Delay Time");
    InsertMenu(hMenu, 1, MF_POPUP|MF_BYPOSITION, (UINT_PTR)hSubMenu, "Settings");
    DestroyMenu(hSubMenu);
	
    DestroyMenu(hMenu);
}
/*******************************************************************************
 * FUNCTION		GetEncoderClsid
 * AUTHOR		MSDN
 * DESCRIPTION
 * Receives the MIME type of an encoder and returns the class identifier 
 * (CLSID) of that encoder
 * SOURCE	http://msdn.microsoft.com/en-us/library/ms533843%28v=VS.85%29.aspx
******************************************************************************/
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo = NULL;

   GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  // Failure

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   {
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      {
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;  // Success
      }    
   }

   free(pImageCodecInfo);
   return -1;  // Failure
}
/*******************************************************************************
 * FUNCTION		ConnectWebcam
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function takes in an int (either 1 or 2 cooresponding to EASYMODE
 * and PHOTOMODE) and reconnects the webcam.
******************************************************************************/
void ConnectWebcam(int mode) {
	// Find out which Mode * you need
	Mode * whichMode;
	if (mode == EASYMODE)
		whichMode = EasyMode;
	else 
		whichMode = PhotoMode;
	// Connect to the driver
	if(	SendMessage((*whichMode).getCapWindow(), WM_CAP_DRIVER_CONNECT,0,0)) {
		SendMessage((*whichMode).getCapWindow(), WM_CAP_SET_SCALE, true , 0);
		SendMessage((*whichMode).getCapWindow(), WM_CAP_SET_PREVIEWRATE, 66, 0);
		SendMessage((*whichMode).getCapWindow(), WM_CAP_SET_PREVIEW, true , 0);
	}
	else {
		MessageBox((*whichMode).getWindow(), "Unable to connect to the driver.\n", 0, 0);
	}
}
/*******************************************************************************
 * FUNCTION		GrabBitmap
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function takes in an int (either 1 or 2 cooresponding to EASYMODE
 * and PHOTOMODE) and grabs a GDI+ Bitmap from the webcam driver from the
 * corresponding camhwnd. It returns a pointer to this Bitmap.
******************************************************************************/
Bitmap* GrabBitmap(int mode) {
	// Find out which Mode * you need
	Mode * whichMode;
	if (mode == EASYMODE)
		whichMode = EasyMode;
	else
		whichMode = PhotoMode;

	//Grab a Frame
	SendMessage((*whichMode).getCapWindow(), WM_CAP_GRAB_FRAME, 0, 0);
	//Copy the frame we have just grabbed to the clipboard
	SendMessage((*whichMode).getCapWindow(), WM_CAP_EDIT_COPY,0,0);

	//Copy the clipboard image data to a HBITMAP object called hbm
	hdc = BeginPaint((*whichMode).getCapWindow(), &ps);
	hdcMem = CreateCompatibleDC(hdc);
	if (hdcMem != NULL)
	{
		if (OpenClipboard((*whichMode).getCapWindow()))
		{
			hbm = (HBITMAP) GetClipboardData(CF_BITMAP);
			SelectObject(hdcMem, hbm);
			GetClientRect((*whichMode).getCapWindow(), &rc);
			CloseClipboard();
		}
	}
	// Create a bitmap from that hbm
	Bitmap *bmp;
	bmp = Bitmap::FromHBITMAP(hbm, NULL);

	// reconnect the webcam
	ConnectWebcam(mode);

	return bmp;
}
/*******************************************************************************
 * FUNCTION		ProcessBitmaps
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function takes in an array of Bitmap pointers, and processes them into
 * a single Bitmap based on the number of shots that are requested.
******************************************************************************/
Bitmap* ProcessBitmaps( Bitmap* bitmaps[] , int numberOfShots) {
	int width = bitmaps[0]->GetWidth();
	int height = bitmaps[0]->GetHeight();

	Bitmap* Result;
	
	if (numberOfShots == 1) {
		Result = new Bitmap(width, height);
		Graphics gr(Result);
		gr.DrawImage(bitmaps[0], 0, 0);
		// free up the memory used
		delete(bitmaps[0]);
	}
	else if (numberOfShots == 2) {
		Result = new Bitmap(width, 2 * height);
		Graphics gr(Result);
		gr.DrawImage(bitmaps[0], 0, 0);
		gr.DrawImage(bitmaps[1], 0, height);
		// free up the memory used
		delete(bitmaps[0]);
		delete(bitmaps[1]);
	}	
	else {
		Result = new Bitmap((int)2*width, (int)2*height);
		Graphics gr(Result);
					
		gr.DrawImage(bitmaps[0], 0, 0);
		gr.DrawImage(bitmaps[1], 0, height);
		gr.DrawImage(bitmaps[2], width, 0);
		gr.DrawImage(bitmaps[3], width, height);
		// free up the memory used
		delete(bitmaps[0]);
		delete(bitmaps[1]);
		delete(bitmaps[2]);
		delete(bitmaps[3]);
	}
	return Result;
} 
/*******************************************************************************
 * FUNCTION		PrintBitmap
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function takes in a pointer to a Bitmap object and prints to the 
 * default printer this Bitmap.
******************************************************************************/
void PrintBitmap(Bitmap* file) {

	DWORD size;
	HDC hdcPrint;
	DOCINFO docInfo;
	ZeroMemory(&docInfo, sizeof(docInfo));
	docInfo.cbSize = sizeof(docInfo);
	docInfo.lpszDocName = "GdiplusPrint";

	// Get the size of the default printer name.
	GetDefaultPrinter(NULL, &size);

	// Allocate a buffer large enough to hold the printer name.
	TCHAR* buffer = new TCHAR[size];

	// Get the printer name.
	if(!GetDefaultPrinter(buffer, &size))
	{
		OutputDebugString("Failure");
	}
	else
	{
      // Get a device context for the printer.
      hdcPrint = CreateDC(NULL, buffer, NULL, NULL);
	  StartDoc(hdcPrint, &docInfo);
      Graphics* graphics;
	  StartPage(hdcPrint);
		graphics = new Graphics(hdcPrint);
		graphics->DrawImage(file, 10, 10, 640, 480);
		delete graphics;
      EndPage(hdcPrint);
	  EndDoc(hdcPrint);
	  DeleteDC(hdcPrint);
	  delete buffer;
	  //OutputDebugString("Printing Finished \n");
	}
}
/*******************************************************************************
 * FUNCTION		ImageProc
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function is run in a seperate thread, it takes in 2 parameters through
 * an ImageProcData struct. Which tell it the number of shots are being 
 * captured, and which mode originated this request. This function controls 
 * the imaging capturing process, it will count down and then take each shot.
 * Once a shot is taken it is then saved to the Local Directory.
******************************************************************************/
DWORD WINAPI ImageProc( LPVOID lpParam ) {
	// acquire mutex
	WaitForSingleObject( 
		hImageProcMutex,    // handle to mutex
        INFINITE);  // no time-out interval
	imageProcIsRunning = true;
	// release mutex
	ReleaseMutex(hImageProcMutex);

	// Cast the parameter to the correct data type.
    // The pointer is known to be valid because 
    // it was checked for NULL before the thread was created.
    PIMAGEPROCDATA pData = (PIMAGEPROCDATA)lpParam;

	Graphics * graphics;
	HWND camhwnd;
	Graphics * capGraphics;

	// If EasyMode requested the capture
	if(pData->mode == EASYMODE) {
		// Make sure there is a valid number of shots being taken
		if (pData->numberOfShots != 0) {
			graphics =  Graphics::FromHWND(EasyMode->getWindow());
			camhwnd = EasyMode->getCapWindow();
			capGraphics =  Graphics::FromHWND(camhwnd);
		
			Bitmap* bitmaps[4];

			for( int i = 0; i < pData->numberOfShots; i++) {
				graphics->DrawImage(images[delayTime],360,535);
				for (int j = 0; j < delayTime; j++) {
					Sleep(1000);
					graphics->DrawImage(images[delayTime - j - 1],360,535);
				}

				// draw the flash
				SendMessage(camhwnd, WM_CAP_SET_PREVIEW, false , 0);
				capGraphics->DrawImage(images[12],0,0);
				Sleep(100); // to prevent "clipping off" the flash image
				SendMessage(camhwnd, WM_CAP_SET_PREVIEWRATE, 34, 0);
				SendMessage(camhwnd, WM_CAP_SET_PREVIEW, true , 0);

				// grab the bitmap
				bitmaps[i] = GrabBitmap(pData->mode);
				// If saving is true, save the bitmap to disk
				if(saving) {
					EasyMode->setShotsTaken(EasyMode->getShotsTaken() + 1);
					SaveBitmap(bitmaps[i], pData->mode);
				}
				// wait a second before immediately going through the next loop
				Sleep(900);
			}
			Bitmap* finalProduct;
			finalProduct = ProcessBitmaps( bitmaps , pData->numberOfShots);
			PrintBitmap( finalProduct );
			// free up the memory used
			delete(finalProduct);
			graphics->DrawImage(images[11],360,535);
		}
	}
	// If PhotoMode requested the capture
	else {
		Bitmap* bitmap;
		graphics =  Graphics::FromHWND(PhotoMode->getWindow());
		camhwnd = PhotoMode->getCapWindow();
		capGraphics =  Graphics::FromHWND(camhwnd);
		graphics->DrawImage(images[delayTime],360,535);
		for (int j = 0; j < delayTime; j++) {
			Sleep(1000);
			graphics->DrawImage(images[delayTime - j - 1],360,535);
		}

		// draw the flash
		SendMessage(camhwnd, WM_CAP_SET_PREVIEW, false , 0);
		capGraphics->DrawImage(images[12],0,0);
		Sleep(100); // to prevent "clipping off" the flash image
		SendMessage(camhwnd, WM_CAP_SET_PREVIEWRATE, 34, 0);
		SendMessage(camhwnd, WM_CAP_SET_PREVIEW, true , 0);

		// grab the bitmap
		bitmap = GrabBitmap(pData->mode);
		SaveBitmap(bitmap, pData->mode);

		graphics->DrawImage(images[11],360,535);
	}
	// free the memory that is passed to the thread
	LocalFree(pData);

	// acquire mutex
	WaitForSingleObject( 
		hImageProcMutex,    // handle to mutex
        INFINITE);  // no time-out interval
	imageProcIsRunning = false;
	// release mutex
	ReleaseMutex(hImageProcMutex);
	return 0; 
}
/*******************************************************************************
 * FUNCTION		LoadImages
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function is run once, at the beginning of the program and the various
 * image resources are loaded into the images[] array for later use.
******************************************************************************/
void LoadImages() {
	images[0] = new Image(L"Resources\\0.png");
	images[1] = new Image(L"Resources\\1.png");
	images[2] = new Image(L"Resources\\2.png");
	images[3] = new Image(L"Resources\\3.png");
	images[4] = new Image(L"Resources\\4.png");
	images[5] = new Image(L"Resources\\5.png");
	images[6] = new Image(L"Resources\\6.png");
	images[7] = new Image(L"Resources\\7.png");
	images[8] = new Image(L"Resources\\8.png");
	images[9] = new Image(L"Resources\\9.png");
	images[10] = new Image(L"Resources\\10.png");
	images[11] = new Image(L"Resources\\null.png");
	images[12] = new Image(L"Resources\\flash.png");
	images[13] = new Image(L"Resources\\curtain.png");
}
/*******************************************************************************
 * FUNCTION		SaveBitmap
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function is run and saves the Bitmap referenced to the users Pictures
 * directory with the following attributes:
 * "Easy Mode MM DD YYYY shot #.bmp
******************************************************************************/
void SaveBitmap(Bitmap* file, int mode) {
	CLSID bmpClsid;

	char* fullFileName = GetFileName(mode);

	// Convert to a wchar_t*
	size_t origsize = strlen(fullFileName) + 1;
	size_t convertedChars = 0;
	wchar_t * wcfullFileName =  new wchar_t[origsize];
	mbstowcs_s(&convertedChars, wcfullFileName, origsize, fullFileName, _TRUNCATE);
	
	GetEncoderClsid(L"image/bmp", &bmpClsid);
	(* file).Save(wcfullFileName, &bmpClsid, NULL);
	// delete memory allocated
	delete wcfullFileName;
	delete fullFileName;
}
/*******************************************************************************
 * FUNCTION		GetMySystemTime
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function returns the system time in a format that I will use:
 * MM-DD-YYYY
******************************************************************************/
char* GetMySystemTime() {
	SYSTEMTIME sysTime;
	GetSystemTime(&sysTime);
	char* buffer = new char[11];
	char temp[5];
	itoa((int)sysTime.wMonth,temp, 10);
	strcpy(buffer, temp);
	strcat(buffer, "-");
	itoa((int)sysTime.wDay,temp, 10);
	strcat(buffer, temp);
	strcat(buffer, "-");
	itoa((int)sysTime.wYear, temp, 10);
	strcat(buffer, temp);
	return buffer;
}
/*******************************************************************************
 * FUNCTION		GetLocalDirectory
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * Returns a string of the local directory, located in 
 * My Documents\FreePhotoBooth
******************************************************************************/
char* GetLocalDirectory() {
	// create a buffer of 3 chars
	int bufferSize = 3;

	char* buffer= new char[bufferSize];
	
	int result = GetEnvironmentVariable("USERPROFILE", buffer, bufferSize);

	// If the needed buffer is greater than 3 chars, create a larger buffer
	// note: it most certainly will need to be larger
	if (result > bufferSize) {
		bufferSize = result;
		delete buffer;
		buffer = new char[bufferSize];
		result = GetEnvironmentVariable("USERPROFILE", buffer, bufferSize);
	}
	// Now append the "My Pictures" directory to the user profile

	// This buffer has enough space for the entire file name
	char* fullFileName = new char[bufferSize+75];

	strcpy(fullFileName, buffer);
	delete buffer;
	strcat(fullFileName, "\\My\ Documents\\FreePhotoBooth\\");

	return fullFileName;
}
/*******************************************************************************
 * FUNCTION		GetLocalDirectory
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function generates the complete filename (char[]) for use in saving the 
 * bitmap to disk.
******************************************************************************/
char* GetFileName(int mode) {
	char* buffer;
	char* fullFileName = GetLocalDirectory();

	if (mode == EASYMODE)
		strcat(fullFileName, "Easy\ Mode\ ");
	else 
		strcat(fullFileName, "Photo\ Mode\ ");

	// append the system time
	char* systemTime;
	systemTime = GetMySystemTime();
	strcat(fullFileName, systemTime);
	delete systemTime;

	// append the shot number, after incrementing the shot #
	strcat(fullFileName, "\ shot\ ");
	buffer= new char[10];
	if (mode == EASYMODE) {
		itoa(EasyMode->getShotsTaken(), buffer, 10);
	}
	else  {
		itoa(PhotoMode->getShotsTaken(), buffer, 10);
	}
	strcat(fullFileName,buffer);
	delete buffer;

	strcat(fullFileName, ".bmp");
	return fullFileName;
}
/*******************************************************************************
 * FUNCTION		StartServer
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function starts the thread which controls the actions of the server.
 * It returns a HANDLE to the thread so that it can be terminated, and cleaned
 * up.
******************************************************************************/
HANDLE StartServer() {
	DWORD dwThreadId;
	HANDLE hServerThread = CreateThread( 
		NULL,                   // default security attributes
		0,                      // use default stack size  
		ServerProc,				// thread function name
		NULL,	// argument to thread function 
		0,						// Use default parameters
		&dwThreadId);			// returns the thread identifier
	// Check the return value for success.
	// If CreateThread fails, terminate execution. 
	// This will automatically clean up threads and memory. 
	if (hServerThread == NULL) 
	{
		ExitProcess(3);
	}
	return hServerThread;
}
/*******************************************************************************
 * FUNCTION		ServerProc
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function is what the server thread executes, it takes in requests from
 * the Printer mode client and performs a lookup on the query. If results are 
 * found it first states how many results there are, then it sends them out.
******************************************************************************/
DWORD WINAPI ServerProc( LPVOID lpParam ) {
	WComm w;
	w.startServer(DEFAULT_PORT);
	OutputDebugString("Server Started........\n");
    while (TRUE) {
        
		// Wait until a client connects
		w.waitForClient();
		OutputDebugString("Client Connected......\n");

		// Work with client
		char rec[50];
		w.recvData(rec,32);

		char *pch, *firstName, *lastName;
		pch = strtok (rec," ");

		// Make sure the request is of the proper format
		if (strcmp(pch, "query") == 0) {
				pch = strtok (NULL, " ");
				firstName = pch;
				pch = strtok (NULL, " ");
				lastName = pch;

				PBDataStruct * link = nullptr;
				int count = 0;
				// check the head to make sure that it is valid
				// and count how many shots are available
				if (head != nullptr) {
						link = head;
						link = head->find(firstName, lastName);
						while (link != nullptr) {
								count++;
								if (link->getNextLink() != nullptr)
										link = link->getNextLink();
								else
										break;
								link = link->find(firstName, lastName);
						}
				}
				// Send how many images are available
				itoa(count, rec, 10);
				w.sendData(rec);
				// Now send the images
				if (count > 0) {
					link = head;
					link = head->find(firstName, lastName);
					while (link != nullptr) {
							w.fileSend(link->getFilepath());
							if (link->getNextLink() != nullptr)
									link = link->getNextLink();
							else
									break;
							link = link->find(firstName, lastName);
					}
				}
		}
		OutputDebugString("Closing COnnection\n");
		w.closeConnection();
	}
	OutputDebugString("It's a Trap!\n");
}
/*******************************************************************************
 * FUNCTION		IPProc
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function controls the dialogue box that pops up when the user wants to 
 * change the IP settings.
******************************************************************************/
BOOL CALLBACK IPProc(HWND hwndDlg, 
                             UINT message, 
                             WPARAM wParam, 
                             LPARAM lParam) 
{
    switch (message) 
    { 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
                    if (!GetDlgItemText(hwndDlg, IDC_IPADDRESS1, ipAddr, 16)) 
                         *ipAddr=0; 
                    // Fall through. 
                case IDCANCEL: 
                    EndDialog(hwndDlg, wParam); 
                    return TRUE; 
            } 
    } 
    return FALSE; 
} 
/*******************************************************************************
 * FUNCTION		DelayTimeProc
 * AUTHOR		John Paul Swaynos
 * DESCRIPTION
 * This function controls the dialogue box that pops up when the user wants to 
 * change the delay time
******************************************************************************/
BOOL CALLBACK DelayTimeProc(HWND hwndDlg, 
                             UINT message, 
                             WPARAM wParam, 
                             LPARAM lParam) 
{
	int newDelayTime = 0;
    switch (message) 
    { 
        case WM_COMMAND: 
            switch (LOWORD(wParam)) 
            { 
                case IDOK: 
                    newDelayTime = GetDlgItemInt(hwndDlg, IDC_EDIT1, NULL, false);
					if (newDelayTime > 0 && newDelayTime <= 10)
						delayTime = newDelayTime;
                    // Fall through. 
                case IDCANCEL: 
                    EndDialog(hwndDlg, wParam); 
                    return TRUE; 
            } 
    } 
    return FALSE; 
} 